import random

import numpy as np
from ortools.sat.python import cp_model

# 高中低热度分别为2，1，0
hot = [0, 1, 2]
layers = 37
probability = 0.5

# 定义货格的层次关系，使用二维列表表示货格布局，即模板号的排列规则
each_row = ['10'] * 4 + ['8'] * 1 + ['10'] * 4 + ['6'] * 2 + ['10'] * 3 + ['8'] * 1 + ['10'] * 4 + ['6'] * 1 + [
    '10'] * 2 + ['8'] * 3 + ['10'] * 4 + ['4'] * 1 + ['10'] * 5 + ['6'] * 2 + ['10'] * 4 + ['8'] * 1 + ['10'] * 4 + [
               '6'] * 2 + ['10'] * 3 + ['8'] * 1 + ['10'] * 4 + ['6'] * 1 + ['10'] * 2 + ['8'] * 3 + ['10'] * 4 + [
               '4'] * 1 + ['10'] * 5 + ['6'] * 1
cargo_grid_mapping = [each_row] * layers

# 定义不同模板号的可放置的货物类型，以及每种货物类型最多放多少货物
template_mapping = {
    "265": {"10": {"265*600*400": 10, "265*600*315": 12, "265*400*400": 15, "265*400*315": 18, "265*400*255": 21},
            "8": {"265*600*400": 8, "265*400*400": 12, "265*400*350": 12, "265*400*255": 15},
            "6": {"265*600*400": 6, "265*400*400": 9, "265*400*350": 12}, "4": {"265*600*400": 4, "265*400*400": 6}},
    "330": {"10": {"330*600*400": 10, "330*600*315": 12, "330*400*400": 15, "330*400*315": 18, "330*400*255": 21},
            "8": {"330*600*400": 8, "330*400*400": 12, "330*400*350": 12, "330*400*255": 15},
            "6": {"330*600*400": 6, "330*400*400": 9, "330*400*350": 12}, "4": {"330*600*400": 4, "330*400*400": 6}},
    "400": {"10": {"400*600*400": 10, "400*600*315": 12, "400*400*400": 15, "400*400*315": 18, "400*400*255": 21},
            "8": {"400*600*400": 8, "400*400*400": 12, "400*400*350": 12, "400*400*255": 15},
            "6": {"400*600*400": 6, "400*400*400": 9, "400*400*350": 12}, "4": {"400*600*400": 4, "400*400*400": 6}},
    "500": {"10": {"500*600*400": 10, "500*600*315": 12, "500*400*400": 15, "500*400*315": 18, "500*400*255": 21},
            "8": {"500*600*400": 8, "500*400*400": 12, "500*400*350": 12, "500*400*255": 15},
            "6": {"500*600*400": 6, "500*400*400": 9, "500*400*350": 12}, "4": {"500*600*400": 4, "500*400*400": 6}}}

high = ['500', '330', '330', '400', '330', '330', '265', '265', '265', '400', '330', '330', '265', '265', '265', '400',
        '330', '330', '265', '265', '265', '400', '330', '330', '265', '265', '265', '400', '330', '330', '265', '265',
        '265', '400', '265', '265', '265']

# # 将所有类型统一到一个字典
# template_mapping_combined = {}
# for key, value in template_mapping.items():
#     template_mapping_combined.update(value)
# # 将所有类型名整理到一个列表
# available_goods_out = list(template_mapping_combined.keys())
# # 定义一个空二维数组
inner_keys = []
for key1 in template_mapping:
    for key2 in template_mapping[key1]:
        inner_keys.extend(template_mapping[key1][key2].keys())
# 将所有类型名整理到一个列表
available_goods_out = []
for key in inner_keys:
    if key not in available_goods_out:
        available_goods_out.append(key)
print(available_goods_out)

# 计算所有模板的货量最大值总和
a = template_mapping['265']
sum_total = 0
for key, value in a.items():
    for key_1, value_1 in value.items():
        sum_total = sum_total + value_1
print(sum_total)

result = np.zeros((layers, len(each_row)), dtype=object)
# 随机为每个货格选择货物类型和货物数量
for row in range(layers):
    for grid in range(len(each_row)):
        template = cargo_grid_mapping[row][grid]
        if random.random() < probability:
            available_goods = list(template_mapping[high[row]][template].keys())
            selected_goods = random.choice(available_goods)
            max_quantity = template_mapping[high[row]][template][selected_goods]
            selected_quantity = random.randint(1, max_quantity)
            result[row, grid] = (
                layers - row, grid + 1, template, selected_goods, selected_quantity, random.choice(hot), high[row])
        else:
            result[row, grid] = (layers - row, grid + 1, template, 0, 0, random.choice(hot), high[row])

# 将结果展开为一维
result_flatten = result.flatten()

# 打印结果，输出为一个二维列表，列表元素为元组,数组内的元素为元组（层数，货格序号，货格号，选取的货物类型，货量，热度，高度）
for i in range(layers):
    print(result[i, :])

# 计算货格总数
element_count = result.size
print(element_count)

# 创建参数nij
N = element_count
M = len(available_goods_out)
nij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 nij 数组
nij[0, 1:] = available_goods_out
nij[1:, 0] = list(range(1, element_count + 1))
for i in range(1, N + 1):  # 将i的范围从1到N+1
    for j in range(1, M + 1):  # 将j的范围从1到M+1
        if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
            if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]]:
                nij[i][j] = template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                    available_goods_out[j - 1]]
            else:
                nij[i][j] = 0
        else:  # 如果货格 i 中已经有货，货物类型为 selected_goods
            if result_flatten[i - 1][3] == available_goods_out[j - 1]:
                max_quantity = template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                    result_flatten[i - 1][3]]
                selected_quantity = result_flatten[i - 1][4]
                nij[i][j] = max_quantity - selected_quantity
            else:
                nij[i][j] = 0  # 其他类型的货物不能放在已经有货的货格中

# 索引nij[1][1]对应于第一行第一列
for i in range(N + 1):
    print(nij[i, :])

# 创建参数mij
mij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 mij 数组
mij[0, 1:] = available_goods_out
mij[1:, 0] = list(range(1, element_count + 1))
d = 3
for i in range(1, N + 1):  # 将i的范围从1到N+1
    for j in range(1, M + 1):  # 将j的范围从1到M+1
        if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
            mij[i][j] = 0
        else:
            if result_flatten[i - 1][3] == available_goods_out[j - 1]:
                if result_flatten[i - 1][4] <= d:
                    mij[i][j] = 1
                else:
                    mij[i][j] = 0
            else:
                mij[i][j] = 0

for i in range(N + 1):
    print(mij[i, :])

# 创建参数ahj,amj,alj,bj
# random_integer = [random.randint(1, 10) for _ in range(len(available_goods_out)*3)]
# random_integer_sum = sum(random_integer)
# random_integer_1 = random_integer[0: len(available_goods_out)]
# random_integer_2 = random_integer[len(available_goods_out): len(available_goods_out)*2]
# random_integer_3 = random_integer[len(available_goods_out)*2: len(available_goods_out)*3]
# random_integer_h = [i/random_integer_sum for i in random_integer_1]
# random_integer_m = [i/random_integer_sum for i in random_integer_2]
# random_integer_l = [i/random_integer_sum for i in random_integer_3]

ahj = np.array(
    [0, 0, 0.017 / 3, 0, 0.03 / 3, 0.096 / 3, 0, 0.005 / 3, 0.022 / 3, 0.001 / 3, 0.448 / 3, 0, 0, 0.158 / 3, 0,
     0.201 / 3, 0, 0, 0, 0.013 / 3, 0, 0.009 / 3, 0, 0, 0])

amj = np.array(
    [0, 0, 0.017 / 3, 0, 0.03 / 3, 0.096 / 3, 0, 0.005 / 3, 0.022 / 3, 0.001 / 3, 0.448 / 3, 0, 0, 0.158 / 3, 0,
     0.201 / 3, 0, 0, 0, 0.013 / 3, 0, 0.009 / 3, 0, 0, 0])

alj = np.array(
    [0, 0, 0.017 / 3, 0, 0.03 / 3, 0.096 / 3, 0, 0.005 / 3, 0.022 / 3, 0.001 / 3, 0.448 / 3, 0, 0, 0.158 / 3, 0,
     0.201 / 3, 0, 0, 0, 0.013 / 3, 0, 0.009 / 3, 0, 0, 0])

bj = np.zeros(M + 1, dtype=float)
for j in range(1, M + 1):
    bj[j] = ahj[j] + amj[j] + alj[j]

print(bj)
print(np.sum(bj))

# 创建参数khi,kmi,kli
khi = np.zeros(N + 1, dtype=int)
for i in range(1, N + 1):
    if result_flatten[i - 1][5] == 2:
        khi[i] = 1
    else:
        khi[i] = 0

kmi = np.zeros(N + 1, dtype=int)
for i in range(1, N + 1):
    if result_flatten[i - 1][5] == 1:
        kmi[i] = 1
    else:
        kmi[i] = 0

kli = np.zeros(N + 1, dtype=int)
for i in range(1, N + 1):
    if result_flatten[i - 1][5] == 0:
        kli[i] = 1
    else:
        kli[i] = 0

# 创建参数o(有货的货格总数）
o = 0
for i in range(1, N + 1):  # 将i的范围从1到N+1
    if result_flatten[i - 1][3] != 0:
        o += 1
print("o=", o)

# 创建参数e
e = 0.05

# 创建参数Q
Q = 100000

# 创建参数f
f = 0.35

# 创建cp-sat模型
model = cp_model.CpModel()

# 变量Xij表示是否选中货格i的货物类型j，采用布尔类型
X = {}
for i in range(1, N + 1):
    for j in range(1, M + 1):
        X[(i, j)] = model.NewBoolVar(f'X_{i}_{j}')

# 约束条件：模板调整涉及到的货量尽量少
model.Add(sum(mij[i][j] - X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1) if mij[i][j] == 1) <= int(e * o))

# 约束条件 - 每个货格只能选择一个货物类型
for i in range(1, N + 1):
    model.Add(sum(X[(i, j)] for j in range(1, M + 1)) == 1)

# 计算总货位数
total_cargo_space = model.NewIntVar(0, sum_total * N, 'total_cargo_space')

# 添加约束条件，计算总货位数
model.Add(total_cargo_space == sum(nij[i][j] * X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1)))

# 约束条件：对各包规,高,中，低热度货位数满足需求
for j in range(1, M + 1):
    model.Add(
        Q * sum(X[(i, j)] * khi[i] * nij[i][j] for i in range(1, N + 1) if mij[i][j] == 0) - total_cargo_space * int(
            Q * f * ahj[j]) >= 0)

for j in range(1, M + 1):
    model.Add(
        Q * sum(X[(i, j)] * kmi[i] * nij[i][j] for i in range(1, N + 1) if mij[i][j] == 0) - total_cargo_space * int(
            Q * f * amj[j]) >= 0)

for j in range(1, M + 1):
    model.Add(
        Q * sum(X[(i, j)] * kli[i] * nij[i][j] for i in range(1, N + 1) if mij[i][j] == 0) - total_cargo_space * int(
            Q * f * alj[j]) >= 0)
#
# # 约束条件：对各包规，提供不同尺寸的模板需要满足:
for j in range(1, M + 1):
    model.Add((sum(X[(i, j)] * khi[i] * nij[i][j] for i in range(1, N + 1)) + sum(
        X[(i, j)] * kmi[i] * nij[i][j] for i in range(1, N + 1)) + sum(
        X[(i, j)] * kli[i] * nij[i][j] for i in range(1, N + 1))) * Q - total_cargo_space * int(Q * f * bj[j]) + sum(
        mij[i][j] * nij[i][j] for i in range(1, N + 1)) >= 0)

# 优化目标 - 最大化总货位数
model.Maximize(total_cargo_space)

# 创建一个cp-sat求解器
solver = cp_model.CpSolver()
solver.parameters.log_search_progress = True
solver.parameters.max_time_in_seconds = 10
# 解决优化问题
status = solver.Solve(model)

# 输出结果
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    selected_templates = {}
    for i in range(1, N + 1):
        for j in range(1, M + 1):
            if solver.Value(X[(i, j)]) == 1:
                selected_templates[i] = available_goods_out[j - 1]
    print("最大化的货位数为:", solver.ObjectiveValue())
    print("每个货格选择的模板：", selected_templates)
else:
    print("无法找到最优解")


def process(input_args: str) -> str:
    # xxx
    output_str = ''
    return output_str


def main():
    mock_data = ''
    result = process(mock_data)
    print(result)


if __name__ == '__main__':
    main()
